package client

import (
	"encoding/json"
	"log"
	"sync/atomic"
	"time"

	"laravel-vue-admin/socket/message"

	"github.com/gorilla/websocket"
)

var clientIDCounter int64

// Client 表示一个WebSocket客户端连接
type Client struct {
	ID     int64
	UserID int64
	Conn   *websocket.Conn
	Send   chan *message.Message
	Hub    interface {
		Unregister(*Client)
		Broadcast(*message.Message)
		SendToUser(int64, *message.Message)
	}
}

// NewClient 创建新的客户端
func NewClient(conn *websocket.Conn, userID int64, hub interface {
	Unregister(*Client)
	Broadcast(*message.Message)
	SendToUser(int64, *message.Message)
}) *Client {
	id := atomic.AddInt64(&clientIDCounter, 1)
	return &Client{
		ID:     id,
		UserID: userID,
		Conn:   conn,
		Send:   make(chan *message.Message, 256),
		Hub:    hub,
	}
}

// ReadPump 从WebSocket连接读取消息
func (c *Client) ReadPump() {
	defer func() {
		c.Hub.Unregister(c)
		c.Conn.Close()
	}()

	// 设置读取超时
	c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
	c.Conn.SetPongHandler(func(string) error {
		c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
		return nil
	})

	for {
		_, data, err := c.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("WebSocket错误: %v", err)
			}
			break
		}

		// 解析消息
		var msg message.Message
		if err := json.Unmarshal(data, &msg); err != nil {
			log.Printf("解析消息失败: %v", err)
			continue
		}

		// 设置发送者信息
		msg.FromUserID = c.UserID
		msg.Timestamp = time.Now().Unix()

		// 处理消息
		c.handleMessage(&msg)
	}
}

// WritePump 向WebSocket连接写入消息
func (c *Client) WritePump() {
	ticker := time.NewTicker(54 * time.Second)
	defer func() {
		ticker.Stop()
		c.Conn.Close()
	}()

	for {
		select {
		case msg, ok := <-c.Send:
			c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			if !ok {
				// Hub关闭了通道
				c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			// 发送消息
			if err := c.Conn.WriteJSON(msg); err != nil {
				log.Printf("写入消息失败: %v", err)
				return
			}

		case <-ticker.C:
			// 发送ping消息保持连接
			c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}

// handleMessage 处理接收到的消息
func (c *Client) handleMessage(msg *message.Message) {
	switch msg.Action {
	case message.ActionPing:
		// 响应ping
		response := &message.Message{
			Type:      message.TypeSystem,
			Action:    message.ActionPong,
			FromUserID: c.UserID,
			Timestamp: time.Now().Unix(),
		}
		select {
		case c.Send <- response:
		default:
		}

	case message.ActionChat:
		// 聊天消息
		if msg.ToUserID > 0 {
			// 私聊
			c.Hub.SendToUser(msg.ToUserID, msg)
			// 同时发送给自己确认
			msg.Status = "sent"
			select {
			case c.Send <- msg:
			default:
			}
		} else {
			// 广播消息
			c.Hub.Broadcast(msg)
		}

	case message.ActionNotify:
		// 通知消息
		if msg.ToUserID > 0 {
			c.Hub.SendToUser(msg.ToUserID, msg)
		} else {
			c.Hub.Broadcast(msg)
		}

	case message.ActionSystem:
		// 系统消息
		c.Hub.Broadcast(msg)

	default:
		log.Printf("未知消息类型: %s", msg.Action)
	}
}

